﻿using System;
using System.Windows;
using System.Windows.Threading;
using DataStore;
using Infrastructure.CurrentData;
using Infrastructure.Services;
using log4net;
using log4net.Config;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.UnityExtensions;
using Microsoft.Practices.Unity;
using NHibernate.Validator.Engine;
using UI.Dialogs;

namespace UI
{
    /// <summary>
    /// Bootstrapper for Unity application
    /// </summary>
    public class Bootstrapper : UnityBootstrapper
    {
        /// <summary>
        /// Configures the <see cref="T:Microsoft.Practices.Unity.IUnityContainer"/>. May be overwritten in a derived class to add specific
        /// type mappings required by the application.
        /// </summary>
        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();

            // Logging
            ILog log = LogManager.GetLogger("System");
            XmlConfigurator.Configure();
            if (log.IsInfoEnabled)
            {
                log.Info("Application starting");
            }

            // Set application unity containers
            Current.Container = Container;
            UnityHelper.Container = Container;

            // CAL registry
            Container.RegisterInstance<ILog>(log, new ContainerControlledLifetimeManager());
            Container.RegisterInstance<IDatabase>(new Database(), new ContainerControlledLifetimeManager());


            // Application exception handler
            Application.Current.DispatcherUnhandledException += AppDispatcherUnhandledException;
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

            var engine = new ValidatorEngine();
            engine.Configure();
            Container.RegisterInstance(engine);
        }

        /// <summary>
        /// Creates the shell or main window of the application.
        /// </summary>
        /// <returns>The shell of the application.</returns>
        /// <remarks>
        /// If the returned instance is a <see cref="T:System.Windows.DependencyObject"/>, the
        /// <see cref="T:Microsoft.Practices.Composite.UnityExtensions.UnityBootstrapper"/> will attach the default <seealso cref="T:Microsoft.Practices.Composite.Regions.IRegionManager"/> of
        /// the application in its <see cref="F:Microsoft.Practices.Composite.Presentation.Regions.RegionManager.RegionManagerProperty"/> attached property
        /// in order to be able to add regions by using the <seealso cref="F:Microsoft.Practices.Composite.Presentation.Regions.RegionManager.RegionNameProperty"/>
        /// attached property from XAML.
        /// </remarks>
        protected override System.Windows.DependencyObject CreateShell()
        {
            MainWindow shell = Container.Resolve<MainWindow>();
            

            // Ribbon services
            RibbonService ribbonService = new RibbonService()
                                              {
                                                  ApplicationMenu = shell.ApplicationMenu
                                              };
            Container.RegisterInstance<IRibbonService>(ribbonService);

            // Window services
            WindowServices windowServices = new WindowServices();
            windowServices.MainWindow = shell;
            Container.RegisterInstance<IWindowServices>(windowServices);


            shell.Show();
            return shell;
        }

        /// <summary>
        /// Returns the module catalog that will be used to initialize the modules.
        /// </summary>
        /// <returns>
        /// An instance of <see cref="T:Microsoft.Practices.Composite.Modularity.IModuleCatalog"/> that will be used to initialize the modules.
        /// </returns>
        /// <remarks>
        /// When using the default initialization behavior, this method must be overwritten by a derived class.
        /// </remarks>
        protected override IModuleCatalog GetModuleCatalog()
        {
            //ModuleCatalog catalog = new ConfigurationModuleCatalog();
            //return catalog;

            return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };
            
        }

        /// <summary>
        /// Handles the UnhandledException event of the CurrentDomain.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.UnhandledExceptionEventArgs"/> instance containing the event data.</param>
        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Exception exception = e.ExceptionObject as Exception;

            LogFatalException(exception);
            ShowExceptionDialog(exception);
        }

        /// <summary>
        /// Unexpected exception handler
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Threading.DispatcherUnhandledExceptionEventArgs"/> instance containing the event data.</param>
        static void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            LogFatalException(e.Exception);

            ExceptionDialog dialog = ShowExceptionDialog(e.Exception);
            if (dialog.CanContinue)
            {
                e.Handled = true;
            }
            else
            {
                Environment.Exit(-1);
            }
        }

        /// <summary>
        /// Shows the unhandled exception dialog.
        /// </summary>
        /// <param name="ex">The exception.</param>
        /// <returns>The dialog box shown on screen.</returns>
        private static ExceptionDialog ShowExceptionDialog(Exception ex)
        {
            ExceptionDialog dialog = new ExceptionDialog();
            if (Application.Current.MainWindow != dialog && Application.Current.MainWindow.Visibility == Visibility.Visible)
            {
                dialog.Owner = Application.Current.MainWindow;
            }
            dialog.Exception = ex;
            dialog.ShowDialog();

            return dialog;
        }

        /// <summary>
        /// Logs the fatal (unhandled) exception.
        /// </summary>
        /// <param name="ex">The exception.</param>
        private static void LogFatalException(Exception ex)
        {
            try
            {
                ILog log = Current.Container.Resolve<ILog>();
                if (log.IsFatalEnabled)
                {
                    log.Fatal(string.Format("Unhandled exception: {0}", ex.Message), ex);
                }
            }
            // ReSharper disable EmptyGeneralCatchClause
            catch { }
            // ReSharper restore EmptyGeneralCatchClause
        }
    }
}
